#include <iostream>
#include <fstream>
using namespace std;


int k;
int n;
bool* used;//[n]
int** data;//[n][k]
int   data_index;
int** result;//[data_index+1][min_value_index+1]
int*  min_value;//[n]
int   min_value_index;

unsigned long long int pow_2 (int k)
{
	unsigned long long int sum = 1;
	for (int i = 0; i < k; i++)
	{
		sum *= 2;
	}
	return sum;
}

int string_2_int (string str)
{
	int sum = 0;
	for (int i = 0; i < str.length(); i++)
	{
		sum *= 10;
		sum += (str[i] - '0');
	}
	return sum;
}

int main (int argc, char* argv[])
{
	string tmp_str = argv[1];
	k = string_2_int(tmp_str);
	n = pow_2 (k);
	cout << "k = " << k << endl;
	cout << "n = " << n << endl;
	// ---- All kinds of new memory array and init value of them
	// used[] , data[][]
	used      = new bool[n];
	data      = new int*[n];
	min_value = new int[n];
	for (int i = 0; i < n; i++)
	{
		data[i] = new int[k];
	}
	for (int i = 0; i < n; i++)
	{
		used[i] = false;
	}
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < k; j++)
		{
			data[i][j] = 0;
		}
	}
	// ---- set value of data and min_value_index
	data_index = -1;
	min_value_index = -1;
	for (int union_index = 1; union_index < n; union_index+=2)
	{
		if (used[union_index % (n-1)] == false)
		{
			// ---- if the first value was not been used,
			// ---- this can concolude that, it can goes on.
			data_index++;
			min_value_index++;
			min_value[min_value_index] = (union_index % (n-1));
			if (min_value[min_value_index] == 0)min_value[min_value_index] = (n-1);
			// ---- Hey!! Give you a flag, judge the length of data[data_index][...]
			bool been_used = false;
			for (int i = 0;i <= (k-1); i++)
			{
				unsigned long long int tmp;
				tmp = union_index * pow_2 (i);
				tmp %= (n-1);
				data[data_index][i] = tmp;
				if (tmp == 0) tmp = (n-1);
				if (used[tmp] == true)
				{
					been_used = true;
				}
				used[tmp] = true;
			}
			// --- if has been_used = true, delete this data[data_index][...]
			// --- and roll back
			if (been_used == true)
			{
				for (int i = 0; i <= (k-1); i++)
				{
					data[data_index][i] = 0;
				}
				data_index--;
			}
		}
	}

	cout << data_index+1 << " " << min_value_index+1 << endl;

	// --- under _DEBUG_ mode, this is -v time.
	string data_file_name = tmp_str + "_data.txt";
	ofstream data_out(&data_file_name[0], ios::out);
	for (int i = 0; i <= data_index; i++)
	{
		for (int j = 0; j < k; j++)
		{
			data_out << data[i][j] << " ";
		}
		data_out << endl;
	}
	data_out.close();
	// --- min_value[...], just for output , under _DEBUG_ mode.
	string min_value_file_name = tmp_str + "_min_value.txt";
	ofstream min_value__out(&min_value_file_name[0], ios::out);
	for (int i = 0; i <= min_value_index; i++)
	{
		min_value__out << min_value[i] << " ";
	}
	min_value__out << endl;
	min_value__out.close();


	// ---- new result[data_index+1][min_value_index+1]
	result = new int*[data_index+1];
	for (int i = 0; i <= data_index; i++)
	{
		result[i] = new int[min_value_index+1];
	}
	
	cout << "----------------------------------------------------------------------------------------------------" << endl;
	for (int i = 0; i <= data_index; i++)
	{
		if (i % 100 == 0)
		{
			int cur_progress = (double)i/data_index*100;
			for (int ii = 0; ii < cur_progress; ii++) cout << "-";
			for (int ii = 0; ii < (100 - cur_progress); ii++) cout << " ";
			cout << "|";
			cout << endl;
		}
		for (int j = 0; j <= min_value_index; j++)
		{
			// calculate result[i][j]
			int count = 0;
			for (int k_idx = 0; k_idx < k; k_idx++)
			{
				int tmp = data[i][k_idx] & min_value[j];
				int cccc = k;
				int tmp_bin_count = 0;
				while (cccc--)
				{
					tmp_bin_count += (tmp % 2);
					tmp /= 2;
				}
				if (tmp_bin_count % 2 == 0)
				{
					count++;
				}
			}
			result[i][j] = count;
		}
	}

	string result_file_name = tmp_str + "_result.txt";
	ofstream out_result(&result_file_name[0], ios::out);
	for (int i = 0; i <= data_index; i++)
	{
		for (int j = 0; j <= min_value_index; j++)
		{
			out_result << result[i][j] << " ";
		}
		out_result << endl;
	}
	out_result.close();

	return 0;
}

